package com.mallcai.bigdata.ladon.service.peekdata.impl;

import com.cake.bigdata.starter.exception.category.BusinessException;
import com.cake.bigdata.starter.exception.error.CommonErrorCode;
import com.mallcai.bigdata.ladon.dpl.entity.common.Datasource;
import com.mallcai.bigdata.ladon.dpl.entity.peekdata.*;
import com.mallcai.bigdata.ladon.dpl.entity.system.User;
import com.mallcai.bigdata.ladon.dpl.mapper.peekdata.ModelMapper;
import com.mallcai.bigdata.ladon.dpl.mapper.peekdata.ModelMetaMapper;
import com.mallcai.bigdata.ladon.dpl.mapper.peekdata.PeekMapper;
import com.mallcai.bigdata.ladon.dpl.mapper.peekdata.PeekRuleMapper;
import com.mallcai.bigdata.ladon.dpl.vo.DataType;
import com.mallcai.bigdata.ladon.dpl.vo.DatasourceType;
import com.mallcai.bigdata.ladon.dpl.vo.PeekRuleType;
import com.mallcai.bigdata.ladon.service.common.IDatasourceService;
import com.mallcai.bigdata.ladon.service.peekdata.IPeekFieldService;
import com.mallcai.bigdata.ladon.service.peekdata.IPeekService;
import com.mallcai.bigdata.ladon.service.peekdata.RuleFactory;
import com.mallcai.bigdata.ladon.service.system.IAccountService;
import com.mallcai.bigdata.ladon.utils.CollectionUtils;
import com.mallcai.bigdata.ladon.vo.DataVo;
import com.mallcai.bigdata.ladon.vo.ExportParam;
import com.mallcai.bigdata.ladon.vo.QueryParam;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tk.mybatis.mapper.util.Assert;

import java.util.*;

/**
 * Created by oneape<oneape15@163.com>
 * Created 2019-05-22 16:42.
 * Modify:
 */
@Service
public class PeekService implements IPeekService {

    @Autowired
    private PeekMapper         peekMapper;
    @Autowired
    private ModelMapper        modelMapper;
    @Autowired
    private PeekRuleMapper     peekRuleMapper;
    @Autowired
    private ModelMetaMapper    modelMetaMapper;
    @Autowired
    private IDatasourceService iDatasourceService;
    @Autowired
    private IAccountService    iAccountService;

    @Autowired
    private IPeekFieldService iPeekFieldService;

    private List<Peek> getByName(String name) {
        Map<String, Object> params = new HashMap<>();
        params.put("peekName", name);

        return getByParams(params);
    }

    @Transactional
    @Override
    public int addPeek(Peek peek, List<PeekRule> rules) {
        Assert.isTrue(StringUtils.isNotBlank(peek.getName()), "取数名称为空");
        Assert.isTrue(StringUtils.isNotBlank(peek.getFields()), "返回数据字段为空");
        Assert.isTrue(rules != null && rules.size() > 0, "筛选规则为空");

        List<Peek> peeks = getByName(peek.getName());
        if (peeks != null && peeks.size() > 0) {
            throw new BusinessException("取数名称已存在");
        }

        peek.setPeekTime(0);
        int status = peekMapper.insert(peek);
        if (status > 0) {
            for (PeekRule pr : rules) {
                pr.setPeekId(peek.getId());
                peekRuleMapper.insert(pr);
            }
        }

        return 1;
    }

    @Transactional
    @Override
    public int editPeek(Peek peek, List<PeekRule> rules) {
        Assert.isTrue(peek.getId() != null, "取数主键为空");
        Assert.isTrue(StringUtils.isNotBlank(peek.getName()), "取数名称为空");
        Assert.isTrue(StringUtils.isNotBlank(peek.getFields()), "返回数据字段为空");
        Assert.isTrue(rules != null && rules.size() > 0, "筛选规则为空");

        List<Peek> peeks = getByName(peek.getName());
        if (peeks != null && peeks.size() > 0) {
            for (Peek p : peeks) {
                if (!p.getId().equals(peek.getId())) {
                    throw new BusinessException("取数名称已存在");
                }
            }
        }

        // 更新取数基本信息
        peekMapper.update(peek);

        // 删除旧的取数据规则
        peekRuleMapper.delByPeekId(peek.getId());

        for (PeekRule pr : rules) {
            pr.setPeekId(peek.getId());
            peekRuleMapper.insert(pr);
        }

        return 1;
    }

    @Transactional
    @Override
    public int deletePeek(Peek peek) {
        Assert.isTrue(peek.getId() != null, "取数主键为空");

        int status = peekMapper.delete(peek);
        if (status > 0) {
            peekRuleMapper.delByPeekId(peek.getId());
        }

        return 1;
    }

    @Override
    public Peek getById(Long peekId) {
        Assert.isTrue(peekId != null, "取数主键为空");
        Peek peek = peekMapper.getById(peekId);
        if (peek != null) {
            List<PeekRule> rules = peekRuleMapper.getByPeekId(peekId);
            peek.setRules(rules);
        }
        return peek;
    }

    @Override
    public List<Peek> getByParams(Map<String, Object> params) {
        return peekMapper.getByParams(params);
    }

    /**
     * 根据取数id获取规则
     *
     * @param peekId Long
     * @return List
     */
    @Override
    public List<PeekRule> getRuleByPeekId(Long peekId) {
        List<PeekRule> prs = peekRuleMapper.getByPeekId(peekId);
        if (prs != null) {
            prs.forEach(pr -> {
                pr.setRuleLabel(PeekRuleType.getDesc(pr.getRule()));
                if (StringUtils.isBlank(pr.getShowName())) {
                    pr.setShowName(pr.getFieldName());
                }
            });
        }
        return prs;
    }

    /**
     * 计算数据条数
     *
     * @param modelId Long 模型Id
     * @param rules   List 规则列表
     * @return Long 数据条数
     */
    @Override
    public Long countDataSize(Long modelId, List<PeekRule> rules) {
        Model model = modelMapper.getById(modelId);
        if (model == null) {
            throw new BusinessException("取数依赖的模型不存在");
        }

        Long dsId = model.getDatasourceId();
        Datasource ds = iDatasourceService.getById(dsId);
        if (ds == null) {
            throw new BusinessException("模型依赖的数据源不存在!");
        }
        QueryParam params = new QueryParam();
        params.setNeedLimit(false);
        StringBuilder sb = new StringBuilder("SELECT COUNT(0) AS size FROM ");
        sb.append(model.getTableName());
        if (rules != null && rules.size() > 0) {
            String whereStr = getRuleString(DatasourceType.getByCode(ds.getType()), modelId, rules);
            if (StringUtils.isNotBlank(whereStr)) {
                sb.append(" WHERE ").append(whereStr);
            }
        }
        params.setRawSql(sb.toString());
        DataVo dataVo = iDatasourceService.execSql(dsId, params);
        if (dataVo != null && dataVo.getRowSize() > 0) {
            Object obj = dataVo.getRows().get(0).get("size");
            if (obj instanceof Long) {
                return (Long) obj;
            } else {
                return Long.valueOf(String.valueOf(obj));
            }
        }
        return 0L;
    }

    /**
     * 预览数据
     *
     * @param modelId Long 模型Id
     * @param fields  返回字段集
     * @param rules   List 规则列表
     * @return DataVo
     */
    @Override
    public DataVo previewData(Long modelId, String fields, List<PeekRule> rules) {
        Model model = modelMapper.getById(modelId);
        if (model == null) {
            throw new BusinessException("取数依赖的模型不存在");
        }

        String[] arr = StringUtils.split(fields, ",");
        List<String> fieldNames = Arrays.asList(arr);
        List<ModelMeta> mms = modelMetaMapper.getByNames(modelId, fieldNames);
        String existFieldNames = "";
        Map<String, String> showNames = new HashMap<>();
        int index = 0;
        for (ModelMeta mm : mms) {
            showNames.put(mm.getName(), mm.getShowName());
            if (index > 0) {
                existFieldNames += ",";
            }
            existFieldNames += mm.getName();
            index++;
        }

        Long dsId = model.getDatasourceId();
        Datasource ds = iDatasourceService.getById(dsId);
        if (ds == null) {
            throw new BusinessException("模型依赖的数据源不存在!");
        }
        QueryParam params = new QueryParam();
        params.setNeedLimit(true);
        params.setLimitSize(10);
        StringBuilder sb = new StringBuilder("SELECT ");
        sb.append(existFieldNames).append(" FROM ").append(model.getTableName());
        if (rules != null && rules.size() > 0) {
            String whereStr = getRuleString(DatasourceType.getByCode(ds.getType()), modelId, rules);
            if (StringUtils.isNotBlank(whereStr)) {
                sb.append(" WHERE ").append(whereStr);
            }
        }
        params.setRawSql(sb.toString());

        DataVo dataVo = iDatasourceService.execSql(dsId, params);
        dataVo.setShowNameOfColumns(showNames);

        return dataVo;
    }

    // 获取查询条件
    private String getRuleString(DatasourceType dt, Long modelId, List<PeekRule> rules) {
        List<String> fieldNames = new ArrayList<>();
        rules.forEach(r -> fieldNames.add(r.getFieldName()));

        List<ModelMeta> mms = modelMetaMapper.getByNames(modelId, fieldNames);
        if (mms == null || mms.size() == 0) {
            return null;
        }
        return RuleFactory.formatRuleString(dt, rules, mms);
    }

    /**
     * 发送数据到当前用户邮箱
     *
     * @param peekId Long
     * @return int 1 - 成功; 0 - 失败
     */
    @Override
    public int sendData2Me(Long peekId) {
        Peek peek = peekMapper.getById(peekId);
        if (peek == null) {
            throw new BusinessException("取数信息不存在");
        }

        Model model = modelMapper.getById(peek.getModelId());
        if (model == null) {
            throw new BusinessException("模型信息不存在");
        }

        Datasource ds = iDatasourceService.getById(model.getDatasourceId());
        if (ds == null) {
            throw new BusinessException("数据源不存在");
        }

        List<PeekRule> rules = peekRuleMapper.getByPeekId(peekId);

        String[] arr = StringUtils.split(peek.getFields(), ",");
        List<String> fieldNames = Arrays.asList(arr);
        List<ModelMeta> mms = modelMetaMapper.getByNames(model.getId(), fieldNames);
        String existFieldNames = "";
        Map<String, String> showNames = new HashMap<>();
        int index = 0;
        for (ModelMeta mm : mms) {
            showNames.put(mm.getName(), mm.getShowName());
            if (index > 0) {
                existFieldNames += ",";
            }
            existFieldNames += mm.getName();
            index++;
        }

        User user = iAccountService.getCurrentUser(); // 获取当前用户
        if (user == null) {
            throw new BusinessException(CommonErrorCode.UNAUTHORIZED.getCode(),"未授权的用户操作");
        }
        if (StringUtils.isBlank(user.getEmail())) {
            throw new BusinessException("403","用户邮箱为空");
        }

        ExportParam params = new ExportParam();
        params.setTitle(peek.getName()); // 设置标题
        params.setEmail(user.getEmail());
        params.setFieldNameToShow(showNames);
        params.setExportFileType("csv");
        params.setFromName("大数据");
        params.setFrom("sxlfgst@163.com");
        params.setNeedLimit(true);
        params.setLimitSize(60 * 10000); // 限制最多导出60万
        StringBuilder sb = new StringBuilder("SELECT ");
        sb.append(existFieldNames).append(" FROM ").append(model.getTableName());
        if (rules != null && rules.size() > 0) {
            String whereStr = getRuleString(DatasourceType.getByCode(ds.getType()), model.getId(), rules);
            if (StringUtils.isNotBlank(whereStr)) {
                sb.append(" WHERE ").append(whereStr);
            }
        }
        params.setRawSql(sb.toString());

        int status = iDatasourceService.asynSendData2Email(ds, params);
        if (status > 0) {
            // 增加取数值
            peekMapper.incPeekNum(peekId);
        }
        return status;
    }

    /**
     * 获取所有数据类型的规则
     *
     * @return Map
     */
    @Override
    public Map<String, List<Map<String, String>>> getAllDataRule() {
        Map<String, List<Map<String, String>>> rules = new HashMap<>();

        for (DataType dt : DataType.values()) {
            List<Map<String, String>> rule = new ArrayList<>();
            List<PeekRuleType> ptrs = RuleFactory.getRuleByDataType(dt);
            ptrs.forEach(ptr -> {
                Map<String, String> map = new HashMap<>();
                map.put("label", ptr.getDesc());
                map.put("value", ptr.getCode());
                rule.add(map);
            });
            rules.put(dt.getCode(), rule);
        }
        List<Map<String, String>> allRules = new ArrayList<>();
        for (PeekRuleType ptr : PeekRuleType.values()) {
            Map<String, String> map = new HashMap<>();
            map.put("label", ptr.getDesc());
            map.put("value", ptr.getCode());
            allRules.add(map);
        }
        rules.put("ALL_RULES", allRules);
        return rules;
    }

    @Transactional
    @Override
    public int saveQuery(Peek peek) {

        checkName(peek);
        int status;
        Long peekId = peek.getId();
        if (peekId == null){
            peek.setPeekTime(0);
            status = peekMapper.insert(peek);
        } else {
            status = peekMapper.update(peek);
        }
        List<PeekRule> rules = peek.getRules();
        List<PeekField> fieldList = peek.getFieldList();

        if (status > 0) {
//            if (peek.getId() != null){
//                List<PeekRule> existedList = peekRuleMapper.getByPeekId(peek.getId());
//                Set<Long> ruleIdSet = CollectionUtils.toSet(existedList, PeekRule::getId);
//                // 还在列表里面的就不能删
//                rules.stream().map(PeekRule::getId).filter(Objects::nonNull).forEach(ruleId-> ruleIdSet.remove(ruleId));
//                ruleIdSet.forEach(peekRuleMapper::deleteById);
//
//                Set<Long> fieldIdSet = CollectionUtils.toSet(iPeekFieldService.getByPeekId(peek.getId()), PeekField::getId);
//                fieldList.stream().map(PeekField::getId).filter(Objects::nonNull).forEach(id-> fieldIdSet.remove(id));
//                fieldIdSet.stream().forEach(iPeekFieldService::deleteById);
//            }

            if (peek.getId() != null){
                peekRuleMapper.delByPeekId(peek.getId());
                iPeekFieldService.deleteByPeekId(peek.getId());
            }
            // 保存规则
            for (PeekRule pr : rules) {
                pr.setPeekId(peek.getId());
                peekRuleMapper.insert(pr);
            }
            //保存取数字段
            fieldList.stream().peek(field->field.setPeekId(peek.getId())).forEach(iPeekFieldService::save);
        }
        return 1;
    }

    private boolean checkName(Peek peek) {
        List<Peek> peeks = getByName(peek.getName());
        boolean isExisted = false;
        if (CollectionUtils.isNotEmpty(peeks)) {
            if (peek.getId() == null){
                isExisted = true;
            } else {
                isExisted = peeks.stream().anyMatch(item -> !item.getId().equals(peek.getId()));
            }
        }
        if (isExisted){
            throw new BusinessException("取数名称已存在");
        }
        return true;
    }

    @Override
    public int incPeekNum(Long peekId) {
        return peekMapper.incPeekNum(peekId);
    }
}
